package com.baishui.beans.factory.xml;

import com.baishui.aop.config.ConfigBeanDefinitionParser;
import com.baishui.beans.BeanDefinition;
import com.baishui.beans.ConstructorArgument;
import com.baishui.beans.PropertyValue;
import com.baishui.beans.factory.config.RuntimeBeanReference;
import com.baishui.beans.factory.config.TypedStringValue;
import com.baishui.beans.factory.support.BeanDefinitionRegistry;
import com.baishui.beans.factory.support.GenericBeanDefinition;
import com.baishui.context.annotation.ClassPathBeanDefinitionScanner;
import com.baishui.core.Resource;
import com.baishui.utils.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

/**
 * v2：单一职责原则
 * 将xml的读取与解析剥离出来
 */
public class XmlBeanDefinitionReader {
	
	public static final String ID_ATTRIBUTE = "id";	

	public static final String CLASS_ATTRIBUTE = "class";

	public static final String SCOPE_ATTRIBUTE = "scope";

	public static final String PROPERTY_ELEMENT = "property";

	public static final String REF_ATTRIBUTE = "ref";

	public static final String VALUE_ATTRIBUTE = "value";

	public static final String NAME_ATTRIBUTE = "name";

	public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";

	public static final String TYPE_ATTRIBUTE = "type";

	public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";

	public static final String CONTEXT_NAMESPACE_URI = "http://www.springframework.org/schema/context";

	private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";

	public static final String AOP_NAMESPACE_URI = "http://www.springframework.org/schema/aop";


	protected final Log logger = LogFactory.getLog(getClass());

	/**
	 * v2：进行Bean的注册，本质就是map的保存
	 */
	BeanDefinitionRegistry registry;
	
	public XmlBeanDefinitionReader(BeanDefinitionRegistry registry){
		this.registry = registry;
	}

	/**
	 * 作用：读取流信息，解析xml文件，并保存bean的定义信息，非实例化bean
	 * @param resource
	 */
	public void loadBeanDefinitions(Resource resource){
		InputStream inputStream = null;
		try {
			/**
			 * 拿到当前的类加载器 TODO
			 */
			//v2-2:ADD 将ClassLoader的实现逻辑放入Resource中
			//ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
			inputStream = resource.getInputStream();
			SAXReader reader = new SAXReader();
			Document doc = reader.read(inputStream);

			Element root = doc.getRootElement(); //<beans>
			Iterator<Element> iter = root.elementIterator();
			while(iter.hasNext()){
				Element ele = (Element)iter.next();
				/**
				 * 判断命名空间
				 */
				String namespaceUri = ele.getNamespaceURI();
				if(this.isDefaultNamespace(namespaceUri)){
					/**
					 * 普通的 bean
					 */
					parseDefaultElement(ele);
				} else if(this.isContextNamespace(namespaceUri)){
					/**
					 * 例如 <context:component-scan>
					 */
					parseComponentElement(ele); //
				} else if(this.isAOPNamespace(namespaceUri)){
					/**
					 * 例如 <aop:config>
					 */
					parseAOPElement(ele);
				}
			}
		}catch (Exception e){
		}finally {
			/**
			 *  需要关闭
			 */
			if(inputStream != null){
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 解析AOP标签
	 * @param ele
	 */
	private void parseAOPElement(Element ele) {

		// TODO 重要
		ConfigBeanDefinitionParser parser = new ConfigBeanDefinitionParser();

		parser.parse(ele, this.registry);
	}

	/**
	 * 解析 <context:component-scan>
	 * @param ele
	 */
	private void parseComponentElement(Element ele) {
		String basePackages = ele.attributeValue(BASE_PACKAGE_ATTRIBUTE);

		/**
		 * ClassPathBeanDefinitionScanner 该对象持有以下实例的作用：
		 * @see ClassPathBeanDefinitionScanner#beanNameGenerator:生成bean的ID
		 * @see ClassPathBeanDefinitionScanner#resourceLoader :将扫描下的class转换为一个个的Resource {@link com.baishui.core.io.support.PackageResourceLoader}
		 * @see BeanDefinitionRegistry
		 */
		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);

		scanner.doScan(basePackages);
	}

	/**
	 *  默认的标签
	 * @param ele
	 */
	private void parseDefaultElement(Element ele) {
		String id = ele.attributeValue(ID_ATTRIBUTE);
		String beanClassName = ele.attributeValue(CLASS_ATTRIBUTE);
		BeanDefinition bd = new GenericBeanDefinition(id,beanClassName);
		/**
		 * 根据xml中的bean的配置信息保存在 BeanDefinition 中
		 */
		if (ele.attribute(SCOPE_ATTRIBUTE)!=null) {
			bd.setScope(ele.attributeValue(SCOPE_ATTRIBUTE));
		}

		/**
		 * constructor-arg 标签解析以及标签中的属性 ref等
		 */
		parseConstructorArgElements(ele,bd);

		/**
		 * property 属性的解析以及标签中的属性 ref等
		 */
		parsePropertyElement(ele,bd);

		registry.registerBeanDefinition(id,bd);
	}


	public boolean isDefaultNamespace(String namespaceUri) {
		return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
	}
	public boolean isContextNamespace(String namespaceUri){
		return (!StringUtils.hasLength(namespaceUri) || CONTEXT_NAMESPACE_URI.equals(namespaceUri));
	}
	public boolean isAOPNamespace(String namespaceUri){
		return (!StringUtils.hasLength(namespaceUri) || AOP_NAMESPACE_URI.equals(namespaceUri));
	}

	public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
		Iterator iter = beanEle.elementIterator(CONSTRUCTOR_ARG_ELEMENT);
		/**
		 * 存在多个 一直循环
		 * @see ConstructorArgument#argumentValues  存在多个的情况
		 */
		while(iter.hasNext()){
			Element ele = (Element)iter.next();

			parseConstructorArgElement(ele, bd);
		}
	}

	public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
		String typeAttr = ele.attributeValue(TYPE_ATTRIBUTE);
		String nameAttr = ele.attributeValue(NAME_ATTRIBUTE);
		/**
		 * 目前只是实现了构造器的ref和String类型注入
		 * @see RuntimeBeanReference
		 * @see TypedStringValue
		 */
		Object value = parsePropertyValue(ele, bd, null);

		ConstructorArgument.ValueHolder valueHolder = new ConstructorArgument.ValueHolder(value);
		if (StringUtils.hasLength(typeAttr)) {
			valueHolder.setType(typeAttr);
		}
		if (StringUtils.hasLength(nameAttr)) {
			valueHolder.setName(nameAttr);
		}

		bd.getConstructorArgument().addArgumentValue(valueHolder);
	}


	public void parsePropertyElement(Element beanElem, BeanDefinition bd) {
		Iterator iter= beanElem.elementIterator(PROPERTY_ELEMENT);
		/**
		 * 存在多个就循环
		 */
		while(iter.hasNext()){
			Element propElem = (Element)iter.next();
			String propertyName = propElem.attributeValue(NAME_ATTRIBUTE);
			if (!StringUtils.hasLength(propertyName)) {
				logger.fatal("Tag 'property' must have a 'name' attribute");
				return;
			}
			/**
			 * 和构造器的解析是一样的
			 */
			Object val = parsePropertyValue(propElem, bd, propertyName);
			PropertyValue pv = new PropertyValue(propertyName, val);
			bd.getPropertyValues().add(pv);
		}
	}

	/**
	 * 目前只是实现了ref和String
	 * @param ele
	 * @param bd
	 * @param propertyName
	 * @return
	 */
	public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
		String elementName = (propertyName != null) ?
				"<property> element for property '" + propertyName + "'" :
				"<constructor-arg> element";

		boolean hasRefAttribute = (ele.attribute(REF_ATTRIBUTE)!=null);
		boolean hasValueAttribute = (ele.attribute(VALUE_ATTRIBUTE) !=null);

		if (hasRefAttribute) {
			String refName = ele.attributeValue(REF_ATTRIBUTE);
			if (!StringUtils.hasText(refName)) {
				logger.error(elementName + " contains empty 'ref' attribute");
			}
			RuntimeBeanReference ref = new RuntimeBeanReference(refName);
			return ref;
		}else if (hasValueAttribute) {
			TypedStringValue valueHolder = new TypedStringValue(ele.attributeValue(VALUE_ATTRIBUTE));
			return valueHolder;
		}
		else {
			throw new RuntimeException(elementName + " must specify a ref or value");
		}
	}
}

